package nets

import (
	"fmt"
	"time"

	"gitee.com/xuender/toolkit/pb"
	"gitee.com/xuender/toolkit/utils"
	"github.com/go-ping/ping"
	"google.golang.org/protobuf/proto"
)

var pingChan = map[uint64]chan *pb.PingResponse{}

// Ping 网络检查.
func Ping(r *pb.PingRequest) uint64 {
	id := utils.RUID()
	c := make(chan *pb.PingResponse, r.Settings.Count+1)
	pingChan[id] = c

	go doPing(r, c)

	return id
}

func doPing(r *pb.PingRequest, c chan *pb.PingResponse) {
	pinger, err := ping.NewPinger(r.Addr)
	if err != nil {
		c <- &pb.PingResponse{
			Type:  pb.PingType_error,
			Error: err.Error(),
		}

		return
	}

	pinger.Count = int(r.Settings.Count)
	pinger.Interval = time.Duration(r.Settings.Interval) * time.Second
	pinger.Size = int(r.Settings.Size)
	pinger.Timeout = time.Duration(r.Settings.Timeout) * time.Second
	pinger.OnFinish = func(s *ping.Statistics) {
		c <- &pb.PingResponse{
			Type: pb.PingType_stats,
			Stats: &pb.PingStats{
				Addr:                  s.Addr,
				IpAddr:                s.IPAddr.String(),
				PacketsSent:           int32(s.PacketsSent),
				PacketsRecv:           int32(s.PacketsRecv),
				PacketsRecvDuplicates: int32(s.PacketsRecvDuplicates),
				PacketLoss:            float32(s.PacketLoss),
				MinRtt:                int64(s.MinRtt),
				AvgRtt:                int64(s.AvgRtt),
				MaxRtt:                int64(s.MaxRtt),
				StdDevRtt:             int64(s.StdDevRtt),
			},
		}
	}
	pinger.OnRecv = func(p *ping.Packet) {
		c <- newResponse(p, false)
	}

	pinger.OnDuplicateRecv = func(p *ping.Packet) {
		c <- newResponse(p, true)
	}

	if err := pinger.Run(); err != nil {
		c <- &pb.PingResponse{
			Type:  pb.PingType_error,
			Error: err.Error(),
		}
	}
}

func newResponse(p *ping.Packet, dup bool) *pb.PingResponse {
	return &pb.PingResponse{
		Type: pb.PingType_packet,
		Packet: &pb.PingPacket{
			Bytes:  int32(p.Nbytes),
			Addr:   p.Addr,
			IpAddr: p.IPAddr.String(),
			Seq:    int32(p.Seq),
			Ttl:    int32(p.Ttl),
			Rtt:    int64(p.Rtt),
			Dup:    dup,
		},
	}
}

// PingCall 网络检查返回.
func PingCall(id uint64) interface{} {
	if c, has := pingChan[id]; has {
		p := <-c
		bs, _ := proto.Marshal(p)

		return bs
	}

	return &pb.PingResponse{
		Type:  pb.PingType_error,
		Error: fmt.Sprintf("未找到: %d", id),
	}
}
